package utils

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	"code.alibaba-inc.com/agit/quarantine-helper/env"

	"code.alibaba-inc.com/agit/quarantine-helper/config"
	"github.com/pkg/errors"
)

// GetCommitIDArray reads commits from reader and returns commit list.
func GetCommitIDArray(reader io.Reader) ([]string, error) {
	var (
		commitIDArray []string
	)

	if reader == nil {
		reader = os.Stdin
	}
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		commitID := scanner.Text()
		commitIDArray = append(commitIDArray, commitID)
	}

	if scanner.Err() != nil {
		return commitIDArray, scanner.Err()
	}
	return commitIDArray, nil
}

func GetCommitByID(commitID string, envs []string) (*GitCommit, error) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	return GetCommit(ctx, commitID, envs)
}

func FilterObjects(oids []string, filter string, env []string) ([]string, error) {
	var (
		ids []string
	)

	if len(oids) == 0 {
		return nil, nil
	}

	cmd := exec.Command("git", "cat-file", "--batch-check=%(objectname) %(objecttype)")
	if config.GetRepositoryPath() != "" {
		cmd.Dir = config.GetRepositoryPath()
	}
	if len(env) > 0 {
		cmd.Env = env
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, errors.New("filter objects: StdinPipe failed")
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, errors.New("filter objects: StdoutPipe failed")
	}
	if err = cmd.Start(); err != nil {
		return nil, errors.New("filter objects: start failed")
	}

	go func() {
		defer stdin.Close()
		for _, oid := range oids {
			if _, err = io.WriteString(stdin, oid); err != nil {
				fmt.Fprintf(os.Stderr, "filter objects: write failed")
				break
			}
			if _, err = io.WriteString(stdin, "\n"); err != nil {
				fmt.Fprintf(os.Stderr, "filter objects: write failed")
				break
			}
		}
	}()

	scanner := bufio.NewScanner(stdout)
	for scanner.Scan() {
		items := strings.SplitN(scanner.Text(), " ", 2)
		if len(items) < 2 {
			continue
		}
		if items[1] != filter {
			continue
		}
		ids = append(ids, items[0])
	}

	if err = cmd.Wait(); err != nil {
		return nil, errors.Errorf("filter objects: wait failed: %s", err)
	}

	return ids, nil
}

// GetLogData get commit info with str
func GetLogData(ctx context.Context, revision string, format string) ([]string, error) {
	var (
		stdout bytes.Buffer
		stderr bytes.Buffer

		envs []string
		ret  []string
	)

	cmd := exec.Command("git", "log", "-1", fmt.Sprintf("--pretty=format:%s", format), revision)

	envs = env.GetEnvsWithQuarantine()

	command, err := NewCommand(ctx, cmd, nil, &stdout, &stderr, envs...)
	if err != nil {
		return nil, err
	}

	if err := command.Wait(); err != nil {
		return nil, fmt.Errorf("stdout: %s, stderr: %s", stdout.String(), stderr.String())
	}

	scanner := bufio.NewScanner(bytes.NewReader(stdout.Bytes()))
	for scanner.Scan() {
		tmp := strings.TrimSpace(scanner.Text())
		if len(tmp) <= 0 {
			continue
		}

		ret = append(ret, tmp)
	}

	return ret, nil
}
